#include "IDirect3DApplication.h"
#include "IDirect3D9Support.h"
#include "IDirect3D10Support.h"

class DXUTDirect3DApplication : public IDirect3DApplication
{
public:
	static DXUTDirect3DApplication *GetInstance();
	virtual ~DXUTDirect3DApplication() { }

	void Initialize();
	int Run();

	virtual IDirect3D9Support *GetDirect3D9Support() const;
	virtual IDirect3D10Support *GetDirect3D10Support() const;
	virtual bool ModifyDeviceSettings(DXUTDeviceSettings* pDeviceSettings);
	virtual void OnFrameMove(double fTime, float fElapsedTime);
	virtual LRESULT MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing);
	virtual void OnKeyboard(UINT nChar, bool bKeyDown, bool bAltDown);
	virtual void OnMouse(bool bLeftButtonDown, bool bRightButtonDown, bool bMiddleButtonDown, bool bSideButton1Down, bool bSideButton2Down, int nMouseWheelDelta, int xPos, int yPos);
	virtual bool OnDeviceRemoved();

private:
	static DXUTDirect3DApplication *s_app;
	static IDirect3D9Support *s_d3d9Support;
	static IDirect3D10Support *s_d3d10Support;

	DXUTDirect3DApplication();

	static void CALLBACK FrameMoveCB(double fTime, float fElapsedTime, void *)
	{ s_app->OnFrameMove(fTime, fElapsedTime); }
	static void CALLBACK KeyboardCB(UINT nChar, bool bKeyDown, bool bAltDown, void *)
	{ s_app->OnKeyboard(nChar, bKeyDown, bAltDown); }
	static void CALLBACK MouseCB(bool bLeftButtonDown, bool bRightButtonDown, bool bMiddleButtonDown,
                       bool bSideButton1Down, bool bSideButton2Down, int nMouseWheelDelta,
                       int xPos, int yPos, void *)
	{ s_app->OnMouse(bLeftButtonDown, bRightButtonDown, bMiddleButtonDown, bSideButton1Down, bSideButton2Down, nMouseWheelDelta, xPos, yPos); }
	static LRESULT CALLBACK MsgProcCB(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool *pbNoFurtherProcessing, void *)
	{ return s_app->MsgProc(hWnd, uMsg, wParam, lParam, pbNoFurtherProcessing); }
	static bool CALLBACK ModifyDeviceSettingsCB(DXUTDeviceSettings* pDeviceSettings, void *)
	{ return s_app->ModifyDeviceSettings(pDeviceSettings); }
	static bool CALLBACK DeviceRemovedCB(void *)
	{ return s_app->OnDeviceRemoved(); }

	static bool CALLBACK IsD3D9DeviceAcceptableCB(D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat,
                                      bool bWindowed, void* pUserContext)
	{ return s_d3d9Support->IsDeviceAcceptable(pCaps, AdapterFormat, BackBufferFormat, bWindowed); }
	static HRESULT CALLBACK OnD3D9CreateDeviceCB(IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                     void* pUserContext)
	{ return s_d3d9Support->OnCreateDevice(pd3dDevice, pBackBufferSurfaceDesc); }
	static HRESULT CALLBACK OnD3D9ResetDeviceCB(IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                    void* pUserContext)
	{ return s_d3d9Support->OnResetDevice(pd3dDevice, pBackBufferSurfaceDesc); }
	static void CALLBACK OnD3D9FrameRenderCB( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
	{ s_d3d9Support->OnFrameRender(pd3dDevice, fTime, fElapsedTime); }
	static void CALLBACK OnD3D9LostDeviceCB( void* pUserContext )
	{ s_d3d9Support->OnLostDevice(); }
	static void CALLBACK OnD3D9DestroyDeviceCB( void* pUserContext )
	{ s_d3d9Support->OnDestroyDevice(); }

	static bool CALLBACK IsD3D10DeviceAcceptableCB( UINT Adapter, UINT Output, D3D10_DRIVER_TYPE DeviceType,
                                       DXGI_FORMAT BackBufferFormat, bool bWindowed, void* pUserContext )
	{ return s_d3d10Support->IsDeviceAcceptable(Adapter, Output, DeviceType, BackBufferFormat, bWindowed); }
	static HRESULT CALLBACK OnD3D10CreateDeviceCB( ID3D10Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
                                      void* pUserContext )
	{ return s_d3d10Support->OnCreateDevice(pd3dDevice, pBackBufferSurfaceDesc); }
	static HRESULT CALLBACK OnD3D10ResizedSwapChainCB( ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
                                          const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
	{ return s_d3d10Support->OnResizedSwapChain(pd3dDevice, pSwapChain, pBackBufferSurfaceDesc); }
	static void CALLBACK OnD3D10FrameRenderCB(ID3D10Device* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
	{ s_d3d10Support->OnFrameRender(pd3dDevice, fTime, fElapsedTime); }
	static void CALLBACK OnD3D10ReleasingSwapChainCB( void* pUserContext )
	{ s_d3d10Support->OnReleasingSwapChain(); }
	static void CALLBACK OnD3D10DestroyDeviceCB( void* pUserContext )
	{ s_d3d10Support->OnDestroyDevice(); }
};
